package com.li.algorithm.leetcode.hot100;


public class Test13 {
    public int maxSubArray(int[] nums) {
        //TODO:如果当前数为正数，加上
        //TODO:如果当前数为负数，且大于sum，统计前一段的结果，重新开始计数
        //TODO:可以小于0，但前提是我手上的筹码值得保留到下一轮，故遇到小于0的值，保存一下当前值
        //TODO:纯负数的序列需要单独计算（取序列中值最大的负数）
        int max = Integer.MIN_VALUE;
        int sum = 0;
        int count = 0;
        int negMax = Integer.MIN_VALUE;
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] > negMax) {
                negMax = nums[i];
            }
            if (nums[i] < 0) {
                count++;
                if (sum > max) {
                    max = sum;
                }
                if (Math.abs(nums[i]) > sum) {
                    sum = 0;
                    continue;
                }
            }
            sum += nums[i];
        }
        if (count == nums.length) {
            return negMax;
        }
        return Math.max(max, sum);
    }

    /**
     * 动态规划
     * f(i) = max{f(i-1)+num[i], num[i]}
     */
    public int maxSubArray2(int[] nums) {
        int pre = 0, max = nums[0];
        for (int num : nums) {
            pre = Math.max(pre+num, num);
            max = Math.max(max, pre);
        }
        return max;
    }

    /**
     * 分冶治之
     */
    public class Status {
        public int lSum, rSum, mSum, iSum;

        public Status(int lSum, int rSum, int mSum, int iSum) {
            this.lSum = lSum;
            this.rSum = rSum;
            this.mSum = mSum;
            this.iSum = iSum;
        }
    }

    public Status pushUp(Status l, Status r) {
        int iSum = l.iSum + r.iSum;
        int lSum = Math.max(l.lSum, l.iSum + r.lSum);
        int rSum = Math.max(r.rSum, r.iSum + l.rSum);
        int mSum = Math.max(Math.max(l.mSum, r.mSum), l.rSum + r.lSum);
        return new Status(lSum, rSum, mSum, iSum);
    }

    public Status getInfo(int[] a, int l, int r) {
        if (l == r) {
            return new Status(a[l], a[l], a[l], a[l]);
        }
        int m = (l + r) / 2;
        Status lSub = getInfo(a, l, m);
        Status rSub = getInfo(a, m + 1, r);
        return pushUp(lSub, rSub);
    }

    public int maxSubArray3(int[] nums) {
        return getInfo(nums, 0, nums.length - 1).mSum;
    }

    /**
     * 「方法二」相较于「方法一」来说，时间复杂度相同，但是因为使用了递归，并且维护了四个信息的结构体，
     * 运行的时间略长，空间复杂度也不如方法一优秀，而且难以理解。那么这种方法存在的意义是什么呢？
     *
     * 对于这道题而言，确实是如此的。但是仔细观察「方法二」，
     * 它不仅可以解决区间 [0,n−1]，还可以用于解决任意的子区间 [l,r] 的问题。
     * 如果我们把 [0,n−1] 分治下去出现的所有子区间的信息都用堆式存储的方式记忆化下来，即建成一棵真正的树之后，
     * 我们就可以在 O(logn) 的时间内求到任意区间内的答案，我们甚至可以修改序列中的值，做一些简单的维护，
     * 之后仍然可以在 O(logn) 的时间内求到任意区间内的答案，
     * 对于大规模查询的情况下，这种方法的优势便体现了出来。这棵树就是上文提及的一种神奇的数据结构——线段树。
     */

    public static void main(String[] args) {
        Test13 test13 = new Test13();
        System.out.println(test13.maxSubArray3(new int[]{-2,1,-3,4,-1,2,1,-5,4}));
    }
}

